#include "playmp3.h"

#include "mad.h"
#include "file.h"
#include "LoopBytes.h"
#include "cpu_peri.h"
#include "typedef.h"
#include "audio_pub.h"
#include "LoopRec.h"

#define MP3_CHANGE_2_CHANNEL


#define debug printf
#define assert(x) if (!(x)) {printf("assert error: %s!\r\n", __FUNCTION__); while (1) {};}

static struct StLoopBytesMgr *pMp3LoopMgr = 0;

//extern struct StBufQueue gbque_mp3;

static struct StMp3Stream gMp3Stream;

static int local_samplerate=16000;
static int local_channel = 1;

static int local_volume=100;

#define BUFFER_MAX (4*1024)

struct SemaphoreLCB* media_stop_sem = 0;
volatile int media_stop_flag = 0;
int media_init()
{
    if (media_stop_sem == 0) {
        media_stop_flag = 0;
        media_stop_sem = semp_init(1,0,"media_stop");
        if (media_stop_sem==0) {
            printf("error: media sem null!\r\n");
            return -1;
        }
    }
    return 0;
}
void media_stop()
{
    printf("media_stop!\r\n");
    media_stop_flag = 1;
}

int media_is_stop()
{
    return media_stop_flag == 1;
}

void media_play()
{
    if (media_stop_flag==1) {
        media_stop_flag = 0;
        if (media_stop_sem) {
            printf("media play!\r\n");
            semp_post(media_stop_sem);
        }
    }
}

uint32_t media_data_send(char *buf, uint32_t len)
{
    if (media_stop_flag == 1 && media_stop_sem) {
        printf("info: media stop!\r\n");
        semp_pendtimeout(media_stop_sem, 0xFFFFFFFF);
    }
    return djy_audio_dac_write(buf, len);
}

int mp3_play_from_file(char *path)
{
    int ret = 0;
    int length = 1024;

    mp3_stream_reset();

    FILE *f = fopen(path, "rb");
    if (f == 0) {
        printf("error: mp3_play_from_file, path=%s!\r\n", path);
        return -1;
    }
    fseek(f, 0L, SEEK_END);
    int file_size = ftell(f);
    fseek(f, 0L, SEEK_SET);
    printf ("info: file size: %d!\r\n", file_size);

    unsigned char *p = (unsigned char*)malloc(length);
    if (p == 0) goto ERROR_RET;

    int n = 0;
    int pos = 0;
    int offset = 0;
    while (1) {
        if (offset >= file_size) break;
        n = fread(p, 1, length, f);
//        printf("mp3_loopbytes_len() is %d\r\n", mp3_loopbytes_len()&(~0x04));
        if (n > 0) {
            offset += n;
            pos = 0;
            while (1) {
                if (pos >= n) break;
                ret = Mp3PlayData(p+pos, n-pos, 5000);
                if (ret > 0){
                    pos += ret;
                }
                else {
                    printf("mp3_play_from_file->Mp3PlayData, ret=%d!\r\n", ret);
                    Djy_EventDelay(300*1000);
                }
            }
        }
        else {
            printf("mp3_play_from_file->fread, n=%d!\r\n", n);
            Djy_EventDelay(300*1000);
        }
    }

ERROR_RET:
    if (f) fclose(f);
    if (p) free(p);
    return 0;
}

typedef struct StMp3Stream {
#if MP3_DEBUG
    unsigned int time_mark;
    volatile unsigned int db_mark_decode;
    volatile unsigned int db_mark_decode_max;
#endif
    volatile unsigned int mp3_duration; //ms
    volatile unsigned int mp3_samplerate;
    volatile unsigned int mp3_bitrate;
    int nReaded;
    int nPos;
    unsigned char buf[BUFFER_MAX];
    int (*getdata)(unsigned char *buf, int len, int timeout);

    //struct StLoopBytesMgr *pMp3LoopMgr;
}StMp3Stream;

void AudioDevReset()
{
    djy_audio_dac_close();
    djy_audio_dac_open(AUDIO_DMA_BUFF_SIZE, local_channel, local_samplerate);
    djy_audio_dac_ctrl(AUD_DAC_CMD_SET_VOLUME, &local_volume);
}


static int  AdjustDelay4DMAPlayEx (unsigned char *frm_data, int frm_len)
{
#ifdef ASO_IMDCT
    #define DECODE_COST_MAX  17
#else
    #define DECODE_COST_MAX  20
#endif
    int n = 0;
#if 0
    struct StMp3Stream *priv = &gMp3Stream;
    static int init_flag = 0;
    int ret = 0;
    int free_size = 0;
    int used_size = 0;
    int t = 0;
    int frm_len_tmp = 0;
    int frm_dur_tmp = 0;
    int left_ms = 0;
    int delay_ms = 0;
    unsigned int frm_dur = priv->mp3_duration;
    LoopRecMgrInit();

    while (1) {
         free_size = audio_dac_ctrl(AUD_DAC_CMD_GET_FREE_BUF_SIZE, 0);
         if (free_size >= frm_len)  {
             //PushFrmRecTail(frm_len, frm_dur);
             LoopRecMgrPushTail(frm_len, frm_dur);
             n = djy_audio_dac_write(frm_data, frm_len);
             assert(n == frm_len);
             break;
         }
         free_size = audio_dac_ctrl(AUD_DAC_CMD_GET_FREE_BUF_SIZE, 0);
         used_size = AUDIO_DMA_BUFF_SIZE - free_size;
         while ((n=GetFrmRecTotalLens()) > used_size) {
             //ret = PullFrmRecTail(&frm_len_tmp, &frm_dur_tmp);
             ret = LoopRecMgrPullHead(&frm_len_tmp, &frm_dur_tmp);
             if (ret < 0) break;
         }
         n = used_size - n;
         if (n > 0 && frm_len_tmp > 0) {
             left_ms = n*frm_dur_tmp/frm_len_tmp;
         }
         t = GetFrmRecTotalDurs();
         delay_ms = t+left_ms-DECODE_COST_MAX;
         if (delay_ms > 0) {
             printf("==delay_ms:%d==!\r\n", delay_ms);
             Djy_EventDelay(delay_ms*1000);
             //printf("info: used_size=%d(B), delay_ms: %d(ms), t_ms=%d(ms), left_ms=%d(ms)!\r\n", used_size, delay_ms, t, left_ms);
         }
         //else{
         //    Djy_EventDelay(1*1000);
         //}
     }
#else
    int writed = 0;
    while (1) {
         n = media_data_send((char*)&frm_data[writed], frm_len-writed);
         //n = djy_audio_dac_write(&frm_data[writed], frm_len-writed);
         if (n > 0) {
             writed += n;
         }
         if (writed < frm_len) {
             Djy_EventDelay(4*1000);
         }
         else {
             break;
         }
     }
#endif
    return 0;
}

void SetBufqOptions(int max_size, int threshold_add, int threshold_get)
{
    (void)max_size;
    static int local_threshold_add=-1;
    static int local_threshold_get=-1;
#if 0
    struct StMp3Stream *pMgr = &gMp3Stream;
    static int local_max_size=0;
    if (local_max_size != max_size) {
        local_max_size = max_size;
        bufque_set_opt(&gbque_mp3, OPT_BYTES_MAX, &local_max_size, 4);
    }
#endif
    if (local_threshold_add != threshold_add) {
        local_threshold_add = threshold_add;
        //bufque_set_opt(&gbque_mp3, OPT_THRESHOLD_ADD, &local_threshold_add, 4);
        if (pMp3LoopMgr) {
            LoopBytesMgrCtrl(pMp3LoopMgr, OPT_LOOPBYTES_SET_THRESHOLDADD, &local_threshold_add, 4);
        }
    }
    if (local_threshold_get != threshold_get) {
        local_threshold_get = threshold_get;
        //bufque_set_opt(&gbque_mp3, OPT_THRESHOLD_GET, &local_threshold_get, 4);
        if (pMp3LoopMgr) {
            LoopBytesMgrCtrl(pMp3LoopMgr, OPT_LOOPBYTES_SET_THRESHOLDGET, &local_threshold_get, 4);
        }
    }
}

void SetAudioOptions(unsigned int channnel, unsigned int samplerate)
{


    if (local_channel != (int)channnel) {
          local_channel = channnel;
          local_samplerate = samplerate;
          djy_audio_dac_close();
          djy_audio_dac_open(AUDIO_DMA_BUFF_SIZE, local_channel, local_samplerate);
//          if (u32g_Volume < 0 ) {
//              u32g_Volume = 0;
//          }
          if (local_volume > 100) {
              local_volume = 100;
          }
          djy_audio_dac_ctrl(AUD_DAC_CMD_SET_VOLUME, &local_volume);
      }
    if (local_samplerate != (int)samplerate) {
        local_samplerate = samplerate;
        switch (samplerate) {
            case    audio_sample_rate_11025:
            case    audio_sample_rate_22050:
            case    audio_sample_rate_44100:
            case    audio_sample_rate_12000:
            case    audio_sample_rate_24000:
            case    audio_sample_rate_48000:
            case    audio_sample_rate_8000:
            case    audio_sample_rate_16000:
            case    audio_sample_rate_32000:
                local_samplerate = samplerate;
                break;
            default:
                local_samplerate = audio_sample_rate_44100;
                break;
        }
        djy_audio_dac_ctrl(AUD_DAC_CMD_SET_SAMPLE_RATE,&local_samplerate);
        debug("==== info: audio_opt, set sample rate: %d! ====\r\n", local_samplerate);
    }
}
int WaitSizeAndTimeOut(int timeout_ms, int size)
{
#define TIME_DELAY_MS 20
    int cnt = 0;

    while (cnt * TIME_DELAY_MS < timeout_ms) {
        Djy_EventDelay(TIME_DELAY_MS * 1000);
        if (LoopBytesMgrTotals(pMp3LoopMgr) > size) {
            break;
        }
        cnt++;
    }
    return 0;
}

static int NetGetData(unsigned char *buffer, int len, int timeout)
{
    int ret = 0;
#if 0
    struct StMp3Stream *pMgr = &gMp3Stream;
    if (pMgr->stop_flag==1 && pMgr->stop_sem) {
        printf("info: mp3 player stop!\r\n");
        semp_pendtimeout(pMgr->stop_sem, 0xFFFFFFFF);
    }
#endif
//    debug("info: %s, bufque_get_data_pend,len=%d, timeout=%d!\r\n", __FUNCTION__, len, timeout);
    //ret = bufque_get_data_pend(&gbque_mp3, buffer, len, timeout, 0);
    if (pMp3LoopMgr) {
        ret = LoopBytesMgrPullPend(pMp3LoopMgr, buffer,  len, timeout, 0);
//        if(ret <= 0)
//        {
////            printf("WaitSizeAndTimeOut\r\n");
//            WaitSizeAndTimeOut(5*1000, 250*1024);
//        }
//        debug("info: %s, bufque_get_data_pend,ret=%d!\r\n", __FUNCTION__, ret);
    }
    return ret;
}



static enum mad_flow header(void *data, struct mad_header const *stream)
{
    struct StMp3Stream *priv = (struct StMp3Stream *)data;
    if (stream) {
        priv->mp3_bitrate = stream->bitrate;
        priv->mp3_samplerate = stream->samplerate;
        //priv->mp3_duration = stream->duration.fraction*1000/352800000UL+stream->duration.seconds*1000;
        priv->mp3_duration = stream->duration.fraction/352800UL+stream->duration.seconds*1000;

        //printf("mp3_duration play(ms)=%d(ms)!\r\n", priv->mp3_duration);
    }
#if MP3_DEBUG
    gMp3Stream.db_mark_decode = GET_TIME_MS();
#endif
    return MAD_FLOW_CONTINUE;
}

static enum mad_flow input(void *data, struct mad_stream *stream)
{
//    enum mad_flow op = MAD_FLOW_CONTINUE;
    int last = 0;
    struct StMp3Stream *priv = (struct StMp3Stream *)data;
    if (!priv->getdata) return MAD_FLOW_STOP;

    if ( stream->buffer == NULL )
    {
        priv->nReaded = priv->getdata(priv->buf, BUFFER_MAX, 0xffffffff);
//        debug("info: %s(%d), getdata, ret=%d!\r\n", __FUNCTION__, __LINE__, priv->nReaded);
        mad_stream_buffer(stream, priv->buf, priv->nReaded);
        return MAD_FLOW_CONTINUE;
    }

    priv->nPos = stream->next_frame - priv->buf;
    last = priv->nReaded - priv->nPos;
    memcpy(priv->buf, priv->buf+priv->nPos, last);
#if 1
    while (1) {
        priv->nReaded = priv->getdata(priv->buf+last, BUFFER_MAX-last, 600);
        if (priv->nReaded < 0) {
    //        debug("info: %s(%d), STOP!\r\n", __FUNCTION__, __LINE__);
            return MAD_FLOW_STOP;
        }
        else if (priv->nReaded == 0) {
            continue;
        }
        else {
            break;
        }

    }
#else
//    debug("info: %s(%d), getdata, ret=%d!\r\n", __FUNCTION__, __LINE__, priv->nReaded);
    priv->nReaded = priv->getdata(priv->buf+last, BUFFER_MAX-last, 0xffffffff);
    if (priv->nReaded <= 0) {
        return MAD_FLOW_STOP;
    }
#endif
    priv->nPos = 0;
    priv->nReaded += last;
    mad_stream_buffer(stream, priv->buf, priv->nReaded);

    return MAD_FLOW_CONTINUE;
}


static inline signed int scale(mad_fixed_t sample)
{
    sample += (1L << (MAD_F_FRACBITS - 16));
    if (sample >= MAD_F_ONE)
        sample = MAD_F_ONE - 1;
    else if (sample < -MAD_F_ONE)
        sample = -MAD_F_ONE;
    return sample >> (MAD_F_FRACBITS + 1 - 16);
}


static enum mad_flow output(void *data, struct mad_header const *header, struct mad_pcm *pcm)
{
    (void)data;
    (void)header;
    struct StMp3Stream *priv= &gMp3Stream;
//    int wrote;
    unsigned int nchannels;
    unsigned int nsamples;
    unsigned int n;
//    unsigned int free_size = 0;
//    unsigned int used_size = 0;
    mad_fixed_t const *left_ch;
    mad_fixed_t const *right_ch;

    unsigned char *ptmp = NULL;
#if 1
    static unsigned char buffer[6912];
#else
    static unsigned char *buffer = 0;
    if (buffer==0) {
        buffer = (unsigned char*)malloc(6912);
    }
    if (buffer==0) {
        return MAD_FLOW_IGNORE;
    }
#endif



    nchannels = pcm->channels;
    n = nsamples = pcm->length;
    left_ch = pcm->samples[0];
#ifdef MP3_CHANGE_2_CHANNEL
    if (nchannels == 1) {
        right_ch = pcm->samples[0];
        nchannels = 2;
    }
    else {
        right_ch = pcm->samples[1];
    }
#else
    right_ch = pcm->samples[1];
#endif
    ptmp = buffer;
    while (nsamples--) {
        signed int sample;
        sample = scale(*left_ch++);
        *(ptmp++) = sample >> 0;
        *(ptmp++) = sample >> 8;
        if (nchannels == 2) {
            sample = scale(*right_ch++);
            *(ptmp++) = sample >> 0;
            *(ptmp++) = sample >> 8;
        }
    }
    if (nchannels == 2)
        n *= 4;
    else
        n *= 2;


    ptmp = buffer;

    //audio_adc_ctrl(AUD_ADC_CMD_SET_CHANNEL, &nchannels);
    SetAudioOptions(nchannels, priv->mp3_samplerate);


#if MP3_DEBUG
    if (gMp3Stream.db_mark_decode_max < GET_TIME_MS()-gMp3Stream.db_mark_decode) {
        gMp3Stream.db_mark_decode_max = GET_TIME_MS()-gMp3Stream.db_mark_decode;
    }
    //printf("mp3 frame decode spend %d(ms), max=%d(ms)!\r\n", GET_TIME_MS()-gMp3Stream.db_mark_decode, gMp3Stream.db_mark_decode_max);
#endif


#if 0
    while (n) {
        wrote = djy_audio_dac_write(ptmp, n);
        ptmp += wrote;
        n -= wrote;
    }
#else

    AdjustDelay4DMAPlayEx (ptmp, n);
#endif
    return MAD_FLOW_CONTINUE;
}


static enum mad_flow error(void *data, struct mad_stream *stream, struct mad_frame *frame)
{
    (void)data;
    (void)stream;
    (void)frame;

    //printf("warning: frame decode return error, and continue flow ...!\r\n");
    return MAD_FLOW_CONTINUE;
}

unsigned int Mp3Framebitrate()
{
    struct StMp3Stream *priv = &gMp3Stream;
    return priv->mp3_bitrate;
}

int getVoice(void)
{
    return  local_volume;
}
int Mp3Voice(int vol)
{
    local_volume = vol;
    djy_audio_dac_ctrl(AUD_DAC_CMD_SET_VOLUME, &local_volume);
    return  local_volume;
}

int AddMp3Voice(int vol)
{
    local_volume += vol;
    djy_audio_dac_ctrl(AUD_DAC_CMD_SET_VOLUME, &local_volume);
    return  local_volume;
}

int DecreaseMp3Voice(int vol)
{
    local_volume -= vol;
    djy_audio_dac_ctrl(AUD_DAC_CMD_SET_VOLUME, &local_volume);
    return  local_volume;
}

int Mp3PlayData(unsigned char *buf, int len, unsigned int timeout)
{
//    struct StMp3Stream *pMgr = &gMp3Stream;
    int opt = 0;
    int ret = 0;
    if (len<=0) return -1;
    opt = 1; //More than one byte will notify to the mp3 play process.
    if (pMp3LoopMgr) {
        LoopBytesMgrCtrl(pMp3LoopMgr, OPT_LOOPBYTES_SET_THRESHOLDADD, &opt, 4);
        opt = 1;// pull_mode == 1 , get it instantly
        LoopBytesMgrCtrl(pMp3LoopMgr, OPT_LOOPBYTES_SET_PULL_MODE, &opt, 4);
        ret = LoopBytesMgrPushPend(pMp3LoopMgr, buf, len, timeout, 0);
    }
    return ret;
}

int mp3_loopbytes_is_empty()
{
    if (pMp3LoopMgr) {
        return LoopBytesMgrTotals(pMp3LoopMgr)==0;
    }
    return 0;
}
int mp3_loopbytes_len()
{
    if (pMp3LoopMgr)
    {
        return LoopBytesMgrTotals(pMp3LoopMgr);
    }

    return 0;
}

void clear_mp3_stream() {
    struct StMp3Stream *pMgr = &gMp3Stream;
#if MP3_DEBUG
    pMgr->time_mark = 0;
    pMgr->db_mark_decode = 0;
    pMgr->db_mark_decode_max = 0;
#endif
    pMgr->mp3_duration = 0; //ms
    pMgr->mp3_samplerate = 0;
    pMgr->mp3_bitrate = 0;
    pMgr->nReaded = 0;
    pMgr->nPos = 0;
    memset(pMgr->buf, 0, BUFFER_MAX);
    //pMgr->getdata = 0;
}

ptu32_t decode_mp3(void)
{

#if 0
    int ret = 0;
    unsigned char buf[16*100];
    while (1) {
        ret = LoopBytesMgrPullPend(buf,  sizeof(buf), 0xFFFFFFFF, 0);
        dumphex16(buf, ret);
    }

#else
//    struct StMp3Stream *pMgr = &gMp3Stream;

    int ret = 0;
    struct mad_decoder decoder;
    while (1) {
        if (pMp3LoopMgr) LoopBytesMgrReset(pMp3LoopMgr);
        clear_mp3_stream();
        gMp3Stream.getdata = NetGetData;


        mad_decoder_init(&decoder, &gMp3Stream, input, header, 0, output, error, 0);
        mad_decoder_options(&decoder, 0);
        ret = mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC);

        mad_decoder_finish(&decoder);
//        Djy_EventDelay(1000*1000);
        printf("--------mp3--------loop!!!\r\n");
    }
#endif
    return ret;
}

int mp3_stream_reset()
{
//    struct StMp3Stream *pMgr = &gMp3Stream;
    if (pMp3LoopMgr) {
        LoopBytesMgrReset(pMp3LoopMgr);
    }
    return 0;
}


int mp3_module_init()
{
    struct StMp3Stream *pMgr = &gMp3Stream;
    memset(pMgr, 0, sizeof(struct StMp3Stream));

    media_init();

    static unsigned char mp3_stack[0x2200];

    djy_audio_dac_open(AUDIO_DMA_BUFF_SIZE, 1, audio_sample_rate_44100);

    //int volume = 60;
    //djy_audio_dac_ctrl(AUD_DAC_CMD_SET_VOLUME, &volume);

    pMp3LoopMgr = LoopBytesMgrInit(500*1024, 256, 500*1024);
  //  pMp3LoopMgr = LoopBytesMgrInitExt(GetMp3BufAddr(), GetMp3BufSize(), 256, 500*1024);
    if (pMp3LoopMgr == 0) {
        printf("error: %s->LoopBytesMgrInit failed!\r\n", __FUNCTION__);
        return -1;
    }

    u16 task_playmp3 = Djy_EvttRegist(EN_CORRELATIVE, CN_PRIO_REAL, 0, 1, decode_mp3, /*NULL*/mp3_stack, sizeof(mp3_stack),"playmp3");
    if(task_playmp3 != CN_EVTT_ID_INVALID)
    {
        Djy_EventPop(task_playmp3,NULL,0,0,0,0);
    }
    return 0;
}


